package com.zbu.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zbu.common.core.constants.CacheConstants;
import com.zbu.common.core.constants.Constants;
import com.zbu.common.core.constants.HttpConstants;
import com.zbu.common.core.domain.LoginUser;
import com.zbu.common.core.domain.R;
import com.zbu.common.core.domain.vo.LoginUserVO;
import com.zbu.common.core.enums.ResultCode;
import com.zbu.common.core.enums.UserIdentity;
import com.zbu.common.core.enums.UserStatus;
import com.zbu.common.core.utils.ThreadLocalUtil;
import com.zbu.common.message.service.AliSmsService;
import com.zbu.common.redis.service.RedisService;
import com.zbu.common.security.exception.ServiceException;
import com.zbu.common.security.service.TokenService;
import com.zbu.friend.domain.user.User;
import com.zbu.friend.domain.user.dto.UserDTO;

import com.zbu.friend.domain.user.dto.UserUpdateDTO;
import com.zbu.friend.domain.user.vo.UserVO;
import com.zbu.friend.manager.UserCacheManager;
import com.zbu.friend.mapper.user.UserMapper;
import com.zbu.friend.service.user.IUserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    TokenService tokenService;
    @Autowired
    RedisService redisService;
    @Autowired
    UserCacheManager userCacheManager;
//    @Resource
//    UserMapper userMapper;
    @Autowired
    private AliSmsService aliSmsService;
    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration;
    @Value("${sms.send-limit:3}")
    private Integer sendLimit;
    @Value("${jwt.secret}")
    private String secret;
    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;
    @Override
    public boolean sendCode(UserDTO userDTO) {
        if(!checkPhone(userDTO.getPhone())){
            throw new ServiceException(ResultCode.FAILED_USER_PHONE);
        }
        String phoneCodeKey=getPhoneCodeKey(userDTO.getPhone());
        Long expire=redisService.getExpire(phoneCodeKey,TimeUnit.SECONDS);
        if(expire!=null&&phoneCodeExpiration*60-expire<60){
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }
        //每天的验证获取次数是50次 第二天次数清零重新计数 计数请0 重新开始计数 怎么存存在哪儿如果大于50抛出异常否则正常执行逻辑
        String codeTimeKey=getCodeTimeKey(userDTO.getPhone());
        Long sendTimes=redisService.getCacheObject(codeTimeKey,Long.class);
        if(sendTimes!=null&&sendTimes>=sendLimit){
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }

        String code=RandomUtil.randomNumbers(6);
        //将验证码与手机号进行绑定促存储到redis中
        redisService.setCacheObject(phoneCodeKey,code,phoneCodeExpiration, TimeUnit.MINUTES);
        aliSmsService.sendMobileCode(userDTO.getPhone(),code);
        redisService.increment(codeTimeKey);
        if(sendTimes==null){//说明是当天第一次发起获取验证码请求
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    public String codeLogin(String phone, String code) {
        //首先判断是新用户还是老用户,如果是新用户则要完成注册加验证验证码,老用户需要进行验证验证码
        checkCode(phone, code);
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if (user == null) {  //新用户
            //注册逻辑
            user = new User();
            user.setPhone(phone);
            user.setStatus(UserStatus.Normal.getValue());
            user.setCreateBy(Constants.SYSTEM_USER_ID);
            userMapper.insert(user);
        }
        return tokenService.createToken(user.getUserId(), secret, UserIdentity.ORDINARY.getValue(), user.getNickName(), user.getHeadImage());
    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) &&
                token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token,secret);
    }

    private void checkCode(String phone, String code) {
        String phoneKey=getPhoneCodeKey(phone);
        String redisCode=redisService.getCacheObject(phoneKey,String.class);
        if(redisCode==null){
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if(!redisCode.equals(code)){
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        redisService.deleteObject(phoneKey);
    }

    public static  boolean checkPhone(String phone){
        Pattern regex = Pattern.compile("^1[2-9][0-9]\\d{8}$");
        Matcher m = regex.matcher(phone);
        return m.matches();

    }
    private  String getPhoneCodeKey(String phone){
        return CacheConstants.PHONE_CODE_KEY+phone;
    }
    private String getCodeTimeKey(String phone){
        return CacheConstants.CODE_KEY+phone;
    }
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if (StrUtil.isNotEmpty(loginUser.getHeadImage())) {
            loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        }
        return R.ok(loginUserVO);

    }
    @Override
    public UserVO detail() {
        Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if(userId==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVO=userCacheManager.getUserById(userId);
        userVO.setHeadImage(downloadUrl+userVO.getHeadImage());
        if(userVO==null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setHeadImage(headImage);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }
}








